/*
 * graph2D
 * Copyright (c) 2009 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#if !defined(___GRAPH2D_SOUND_PLAYER_H___)
#define ___GRAPH2D_SOUND_PLAYER_H___

#include "common.h"
#if defined(TARGET_APPLE)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#elif defined(TARGET_WINDOWS)
#include <al.h>
#include <alc.h>
#pragma comment(lib,"OpenAL32.lib")
#endif
#include <string>

#if (defined(DEBUG) || defined(_DEBUG) || defined(_DEBUG_)) || !defined(NDEBUG)
#include <libmana.h>
#define GRAPH2D_CHECK_AL_ERROR() {		\
		ALenum error = alGetError();	\
		if(error != AL_NO_ERROR){		\
			MANA_ERROR("%s(%d): OpenAL Error #%x\n", __FILE__, __LINE__, error); \
		}								\
	}
#define GRAPH2D_CALL_AL(FUNC) {		\
		FUNC;						\
		GRAPH2D_CHECK_AL_ERROR();	\
	}
#else
#define GRAPH2D_CHECK_AL_ERROR() ((void)0)
#define GRAPH2D_CALL_AL(FUNC) FUNC
#endif

namespace Graph2D
{
	namespace Sound
	{
		class DataManager
		{
		public:
			DataManager();
			virtual ~DataManager();

			void initialize(const size_t size);
			void finalize();

			size_t load(const std::string& filename);
			void release(const size_t index);

			bool getLoop(const size_t index) const;
			void setLoop(const size_t index, const bool enable);

			size_t getLoopPoint(const size_t index) const;
			void setLoopPoint(const size_t index, const size_t sampling);

			ALuint name(const size_t index);

		private:
			typedef struct Environment
			{
				unsigned int flag;
				ALint samplingLoopPoint;
			}Environment;

			ALuint* buffers;
			unsigned char* allocated;
			Environment* environments;
			size_t size;
		};

		class Listener
		{
		public:
			Listener();

			void setPosition(const float x, const float y, const float z);
			void setVelocity(const float x, const float y, const float z);
		};

		typedef struct Parameter
		{
			ALfloat position[3];
			ALfloat velocity[3];
			ALfloat direction[3];
		}Parameter;

		class Player
		{
		public:
			Player();
			virtual ~Player();

			void initialize(const size_t size, DataManager* dataManager);
			void finalize();
			void update(const float second);

			void stopAll(const float second);
			void pauseAll(const bool on, const float second);

			size_t play(const size_t data);
			size_t play(const size_t data, const Parameter& parameter);
			void stop(const size_t index, const float second);
			void rewind(const size_t index);
			void pause(const size_t index, const bool on, const float second);
			void apply(const Parameter& parameter);
			void apply(const size_t index, const Parameter& parameter);

			float getVolume(const size_t index) const;
			void setVolume(const size_t index, const float second, const float value);
			float getPitch(const size_t index) const;
			void setPitch(const size_t index, const float second, const float value);

		private:
			size_t find();
			void playDirect(const size_t index, const size_t data);

		private:
			typedef struct Environment
			{
				unsigned int flag;
				size_t dataManagerIndex;
				struct Fade
				{
					float current;
					float target;
					float velocity;
					float timer;
				}fade[2];
				float savedVolume;
			}Environment;

			DataManager* dataManager;
			Environment* environments;
			ALuint* sources;
			size_t size;
		};

		class Actor
		{
		public:
			Actor();

			void initialize(Player* player);
			void finalize();

			void stopAll(const float second);
			void pauseAll(const bool on, const float second);

			size_t play(const size_t data);
			void stop(const size_t index, const float second);
			void rewind(const size_t index);
			void pause(const size_t index, const bool on, const float second);

		private:
			Player* player;
			Parameter parameter;
		};

		class MusicPlayer
		{
		public:
			MusicPlayer();
			virtual ~MusicPlayer();

			void initialize(DataManager* dataManager);
			void finalize();

			void stopAll(const float second);
			void pauseAll(const bool on, const float second);

			size_t play(const size_t data);
			size_t change(const size_t data, const float second);
			void stop(const size_t index, const float second);
			void pause(const size_t index, const bool on, const float second);

			float getVolume(const size_t index) const;
			void setVolume(const size_t index, const float second, const float value);
			float getPitch(const size_t index) const;
			void setPitch(const size_t index, const float second, const float value);

		private:
			Player player;
			unsigned char current;
			unsigned char pad[3];
		};

		bool initialize();
		void finalize();
		void validate();
		void update(const float second);

		Listener& getListener();
	}
}

#endif
